#include <functional> // For std::function
#include <QtCore/QSysInfo>
#include <QtCore/QString>
#include <QtCore/QProcess>
#include <QtCore/QUrl>
#include <QtCore/QStringListModel>
#include <QtGui/QStandardItemModel>
#include <QtGui/QStandardItem>
#include <QtGui/QDesktopServices>
#include <QtGui/QCursor>
#include "includes.txt"

const QString Title( "QWidgets All" );
QString CurrentFolder(".");

//只添加UI的组件，不涉及到辅助的组件部分。
void addLayOutWidgets(QLayout * layout){
	//1:QCalendarWidget 
	{
		// 创建日历控件
		QCalendarWidget *calendar = new QCalendarWidget();
		calendar->setGridVisible(true);

		// 设置日期范围（如果需要）
		calendar->setMinimumDate(QDate(2000, 1, 1));
		calendar->setMaximumDate(QDate(2100, 12, 31));

		// 连接信号和槽来处理日期选择变化（如果需要），这里主要是lambda对应的是function<void()>
		//注释掉这个函数就不崩了，应该还是capture的问题。[&](){}这个虽然是指针，但是生命周期过去了？
		//别捕捉临时的指针了，指向指针的值，下面这种方式就可以 [calendar]()mutable //看来要重新学习capture知识了
		//传统的QPointer 也行，但是这个用法有点老了。
		QObject::connect(calendar, &QCalendarWidget::selectionChanged, [calendar]()mutable {
			QDate date = calendar->selectedDate();
			// 处理选定日期，例如打印出来
			qDebug() << "Selected date:" << date.toString(Qt::ISODate);
		});
		layout->addWidget(calendar);
	}
	//2：QCheckBox
	{
		QCheckBox *checkBox = new QCheckBox("Enable feature X");
		layout->addWidget(checkBox);
		
		// 连接信号槽来响应复选框的状态变化 对应lambda为function<void(int)>
		QObject::connect(checkBox, &QCheckBox::stateChanged, [&](int state){
			if (state == Qt::Checked) {
				qDebug() << "Feature X enabled";
			} else {
				qDebug() << "Feature X disabled";
			}
		});
		layout->addWidget(checkBox);
	}

	//3：QColumnView 这个应该要多看看，列显示。多列层次结构视图。
	{
		// 创建一个标准项模型
		QStandardItemModel* model = new QStandardItemModel();

		// 添加一些层次数据
		QStandardItem *rootNode = model->invisibleRootItem();

		// 父项
		QStandardItem *americaItem = new QStandardItem("America");
		QStandardItem *europeItem = new QStandardItem("Europe");

		rootNode->appendRow(americaItem);
		rootNode->appendRow(europeItem);

		// 子项
		QStandardItem *canadaItem = new QStandardItem("Canada");
		americaItem->appendRow(canadaItem);

		QStandardItem *italyItem = new QStandardItem("Italy");
		europeItem->appendRow(italyItem);
		
		//子项的子项
		QStandardItem *torontoItem = new QStandardItem("Toronto");
		canadaItem->appendRow(torontoItem);
		// 创建 QColumnView 并设置其模型
		QColumnView* columnView = new QColumnView();
		columnView->setModel(model);
		layout->addWidget(columnView);
		//注意默认的情况，最后一级空也会显示。
	}
	//4：QComboBox，下拉列表
	{
		QComboBox * comboBox = new QComboBox();
		//add items
		comboBox->addItem("Apple");
		comboBox->addItem("Banana");
		comboBox->addItem("Cherry");
		
		auto* label = new QLabel("Select an item...");
		layout->addWidget(label);
		
		QObject::connect(comboBox, &QComboBox::currentTextChanged,
        [label](const QString &text){ label->setText(QString("Selected Item: %1").arg(text)); });
        layout->addWidget(comboBox);
	}
	//5:QCommandLinkButton,这是一个带有附加跳转的跳转button而已
	{
		QCommandLinkButton* commandLinkButton = new QCommandLinkButton();
		commandLinkButton->setText("Button Title");
		commandLinkButton->setDescription("Description of the action that this button will perform.");
		
		QObject::connect( commandLinkButton,&QCommandLinkButton::clicked,[](){
			qDebug() << "QCommandLinkButton::clicked";
			//打开一个URL
			QDesktopServices::openUrl(QUrl("https://doc.qt.io/qt-6/create-your-first-applications.html"));
			
			// 创建 QProcess 对象
			QProcess *process = new QProcess();

			// 设置要运行的命令
			QString program = "cmd.exe";
			QStringList arguments;
			arguments << "/c" << "dir /s /b";

			// 启动进程 start就是直接启动了，输出不显示
			process->startDetached(program, arguments);//绑定启动，直接启动就是结果未知 看是否完成了

			// 可以选择等待进程结束
			// process->waitForFinished();

			// 处理进程完成时的逻辑
			QObject::connect(process, static_cast<void(QProcess::*)(int, QProcess::ExitStatus)>(&QProcess::finished),
					[process](int exitCode, QProcess::ExitStatus exitStatus) {
						if (exitStatus == QProcess::NormalExit && exitCode == 0) {
							qDebug() << "Command executed successfully.";
						} else {
							qDebug() << "Command failed.";
						}
						
						// 完成后清理
						process->deleteLater();
					});
		});
		layout->addWidget(commandLinkButton);
	}
	//6:QDateTimeEdit,用于允许用户输入日期和时间。它继承自QAstractSpinBox类，因此拥有所有标准的微调框功能
	{
		QDateTimeEdit *dateTimeEdit = new QDateTimeEdit;
		dateTimeEdit->setDateTime(QDateTime::currentDateTime());
		
		dateTimeEdit->setDisplayFormat("yyyy-MM-dd HH:mm:ss");
		dateTimeEdit->setMinimumDate(QDate::currentDate().addDays(-365)); // 限制为过去一年
		dateTimeEdit->setMaximumDate(QDate::currentDate().addDays(365)); // 限制为未来一年
		
		QObject::connect(dateTimeEdit, &QDateTimeEdit::dateTimeChanged, [](const QDateTime &dateTime){
			qDebug() << "New Date and Time selected:" << dateTime.toString();
		});
		layout->addWidget(dateTimeEdit);
	}
	//7:QDial,类似拨盘用来调整参数的控件，应该要改一下效果，上面没有数码显示
	{
		QDial *dial = new QDial;
		dial->setRange(0, 100);  // 设置范围从0到100
		dial->setSingleStep(1);  // 设置单步大小为1
		dial->setValue(50);       // 初始化时设置为50
		dial->setNotchesVisible(true); // 显示刻度
		
		QObject::connect(dial, &QDial::valueChanged, [=](int value){
			qDebug() << "Dial value changed to:" << value;
		});
		layout->addWidget(dial);
	}
	//8：QKeySequenceEditor：用来捕获和编辑键盘快捷键序列。（一种是设置的方式，一种API的set功能）
	{
		QKeySequenceEdit *keySequenceEdit = new QKeySequenceEdit();//数量跟间隔有限制
		layout->addWidget(keySequenceEdit);
		QObject::connect(keySequenceEdit, &QKeySequenceEdit::keySequenceChanged, [](const QKeySequence &sequence){
			// 处理键位序列变更
			qDebug() << "New key sequence:" << sequence.toString(QKeySequence::NativeText);
		});
	}
	//9：QLCDNumber 能够以LCD风格来展示数字。
	{
		QLCDNumber *lcdNumber = new QLCDNumber();
		lcdNumber->setDigitCount(5);
		lcdNumber->display(12345); // 显示整数
		//lcdNumber->display(12.345); // 显示浮点数
		lcdNumber->setSegmentStyle(QLCDNumber::Flat);//样式
		layout->addWidget(lcdNumber);
		//关其他的类型的情况，就要绑定事件，但是应该跟time这种就不贴合。
	}
	//10：QListView，表现为List的界面，应该可以类型相同，这种都是Group
	{
		QListView *listView = new QListView();
		QStringListModel *model = new QStringListModel();
		model->setStringList(QStringList() << "Item 1" << "Item 2" << "Item 3");
		listView->setModel(model);
		//配置视图（可选）
		listView->setSelectionMode(QAbstractItemView::SingleSelection);
		listView->setDragEnabled(true);
		listView->setAcceptDrops(true);
		listView->setDropIndicatorShown(true);
		
		//信号处理，输出选中的index，然后根据index来处理data
		QObject::connect(listView, &QListView::clicked, [](const QModelIndex &index){
			qDebug() << "You clicked on item:" << index.row();
		});
		
		//添加到layout上
		layout->addWidget(listView);
	}
	//11：QListWidget，类似QListView
	{
		QListWidget *listWidget = new QListWidget();
		//很多种添加Items的方式，其实用迭代器更好吧，不够generic~_~
		listWidget->addItems(QStringList() << "Item 1" << "Item 2" << "Item 3");
		listWidget->setSelectionMode(QAbstractItemView::SingleSelection);
		
		QObject::connect(listWidget, &QListWidget::itemClicked, [](QListWidgetItem *item){
			qDebug() << "You clicked on item:" << item->text();
		});
		//添加到layout上
		layout->addWidget(listWidget);
	}
}

//无法独立使用GUI控件，或者主要目标是辅助类型的，或者顶级窗口，比如带Dialog的类型
void addLayoutCommonSettings(QLayout * layout, QWidget* window,QApplication * app){
	//1.ButtonGroup
	{
		auto* label = new QLabel("Just for ButtonGroup:");
		layout->addWidget(label);
		// 创建按钮并添加到布局中
		QRadioButton *radio1 = new QRadioButton("Option 1");
		QRadioButton *radio2 = new QRadioButton("Option 2");
		QRadioButton *radio3 = new QRadioButton("Option 3");
		//添加在layout 可视化
		layout->addWidget(radio1);
		layout->addWidget(radio2);
		layout->addWidget(radio3);
		// 创建QButtonGroup并添加按钮
		QButtonGroup *buttonGroup = new QButtonGroup(window);
		buttonGroup->addButton(radio1);
		buttonGroup->addButton(radio2);
		buttonGroup->addButton(radio3);

		// 设置默认选择
		radio1->setChecked(true);
	}
	//1:QColorDialog,因为是弹出式界面，加个CheckBox把
	{
		QPushButton* button = new QPushButton("Select Color", window);
		layout->addWidget(button);
		
		// 连接信号槽来响应复选框的状态变化 对应lambda为function<void()>
		QObject::connect(button, &QPushButton::clicked, [window](){
			QColor color = QColorDialog::getColor(Qt::white, window);
			if (color.isValid()) {
				QPalette palette = window->palette();
				palette.setColor(QPalette::Window, color);
				window->setPalette(palette);
			}
		});
		layout->addWidget(button);
	}
	//2:QColormap,提供类在Qt中提供了对系统颜色映射表的访问，一般来说只是显示用用，为什么是UI组件呢？
	{
		//通过QColormap::instance的方式来访问，没有界面弹出，就不写了。
	}
	//3：QCommonstyle 全局Style设置。
	{
		QComboBox * comboBox = new QComboBox(window);
		//add items
		QStringList availableStyles = QStyleFactory::keys();
		
		for(const QString &item:availableStyles){
			comboBox->addItem(item);
		}
		
		QObject::connect(comboBox, &QComboBox::currentTextChanged,
			[app](const QString &text){ 
				//QApplication *app = qobject_cast<QApplication *>(QApplication::instance());
				app->setStyle(text);
			});
        layout->addWidget(comboBox);
	}
	//4：QCompleter 自动补全，要跟输入结合使用，嗯，字典也要有,只有一行输入的前部有用 后面就不行了 比如输入apple banana。
	{
		QLineEdit *lineEdit = new QLineEdit;
		layout->addWidget( lineEdit );
		
		// 创建 QStringList 来保存候选词
		QStringList completionList = QStringList() << "apple" << "banana" << "cherry" << "date";

		// 创建并配置 QStringModel
		QStringListModel *stringListModel = new QStringListModel(completionList, lineEdit);

		// 创建 QCompleter 并将其与 QLineEdit 绑定
		QCompleter *completer = new QCompleter(stringListModel, lineEdit);
		completer->setCaseSensitivity(Qt::CaseInsensitive);  // 设置大小写不敏感
		completer->setCompletionMode(QCompleter::PopupCompletion); // 设置为弹出补全模式
		lineEdit->setCompleter(completer);
	}
	//5：QDialog 是顶级窗口，添加在另一个窗口的布局中是不行。
	{
		QPushButton* button = new QPushButton("Show Dialog");
		
		// 连接信号槽来响应复选框的状态变化 对应lambda为function<void()>
		QObject::connect(button, &QPushButton::clicked, [window](){
			QDialog* dialog = new QDialog;
			dialog->setWindowTitle("Simple Dialog");
			dialog->setModal(true); // 设置为模态对话框
			dialog->setAttribute(Qt::WA_DeleteOnClose);//关闭时，Qt自动删除它。
			
			dialog->exec();//直接show 出来。
		});
		layout->addWidget(button);
	}
	
	//6：QDialogButtonBox 根据定义是给Dialog添加的按钮，为什么不是普通Button呢？功能集合品。
	{
		
		
		QPushButton* button = new QPushButton("Show QDialogButtonBox");
		
		// 连接信号槽来响应复选框的状态变化 对应lambda为function<void()>
		QObject::connect(button, &QPushButton::clicked, [window](){
			QDialog* dialog = new QDialog; //感觉这些内容都可以直接定义一个新类了，Widget 本身就是Widget Composer。
			dialog->setWindowTitle("Simple Dialog");
			dialog->setModal(true); // 设置为模态对话框
			dialog->setAttribute(Qt::WA_DeleteOnClose);//关闭时，Qt自动删除它。
			
			// 创建一个 QVBoxLayout
			QVBoxLayout *tempLayout = new QVBoxLayout(dialog);
			
			QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);

			// 将按钮盒添加到布局中
			tempLayout->addWidget(buttonBox);

			// 连接按钮信号到 QDialog 的 accept() 和 reject() 槽
			QObject::connect(buttonBox, &QDialogButtonBox::accepted, dialog, &QDialog::accept);
			QObject::connect(buttonBox, &QDialogButtonBox::rejected, dialog, &QDialog::reject);
			
			// 设置对话框的布局
			dialog->setLayout(tempLayout);
			
			//dialog->move(QCursor::pos());//临时加的，并不一定要这样设计，不应该锁定位置的。
			dialog->move(window->frameGeometry().topLeft() + window->rect().center() - dialog->rect().center());//放在布局器之后吧 效果都不好！！
			
			dialog->exec();//直接show 出来。
		});
		layout->addWidget(button);
	}
	//7：QDockWidget 要跟QMainWindow配合使用的，留在下一个demo中展示了。
	{
	}
	//8：QFileDialog：弹出式界面，然后从文件路径中选择文件名的路径，或者文件夹
	{
		QPushButton* button = new QPushButton( "Open Image" );		
		// 连接信号槽来响应复选框的状态变化 对应lambda为function<void()>
		QObject::connect(button, &QPushButton::clicked, [window](){
			auto fileName = QFileDialog::getOpenFileName(window, 
			"Open Image",
			CurrentFolder,
			"Images (*.png *.xpm *.jpg)");
			if( !fileName.isEmpty() )
			{
				auto info = QFileInfo(fileName);
				CurrentFolder = info.path();
				qDebug() << "Find Image."<<fileName<<"\tDirectory:"<<CurrentFolder<<"\n";
			}else {
				CurrentFolder = ".";
			}
		});
		layout->addWidget(button);
	}
	//9:QFileIconProvider：如果你坚持要使用 QFileIconProvider  	并且希望它能够显示自定义图标，你需要确保操作系统能够识别你的图片文件，并且已经有了一个默认的图标与之关联。
	// 但通常这样做不太实际，除非你正在处理系统文件类型的图标。对于自定义图像，你应当直接使用 QPixmap 和 QIcon 类。
	{
		QFileIconProvider iconProvider;
		//这个文件路径不行啊 
		QFileInfo fileInfo("icons8-see-no-evil-monkey-48.png");
		//QFileInfo fileInfo(QCoreApplication::applicationDirPath() + "/icons8-see-no-evil-monkey-48.png");
		
		QPixmap pixmap("icons8-see-no-evil-monkey-48.png"); //OK
		if(pixmap.isNull()) {
			qDebug() << "图标加载失败，请检查路径是否正确";
		}
	
		QIcon fileIcon = iconProvider.icon(fileInfo);
		
		if( fileIcon.isNull() )
		{
			qDebug() << "QFileIconProvider 返回了无效的 QIcon";
		}
		
		QLabel* label = new QLabel;
		//label->setPixmap( fileIcon.pixmap(48,48) );//fileIcon 要系统的设置，暂时找不到对应的情况。
		label->setPixmap( pixmap );
		
		layout->addWidget(label);
	}
	//10：QFocusFrame,全局的，跟踪当前的widgets
	{
		// 创建 QFocusFrame 对象，parent 设置为window
		QFocusFrame *focusFrame = new QFocusFrame(window);
		focusFrame->setStyleSheet("QFocusFrame { border: 2px solid green; }");//焦点框架的边框设置为2像素宽且颜色为绿色。
		
		// 当焦点改变时更新 QFocusFrame，注意，lambda capture的情况，&focusFrame 会dump的啊
		QObject::connect( app, &QApplication::focusChanged, [focusFrame](QWidget *old, QWidget *now) {
			// 我们只关心新获得焦点的控件
			if (now) {
				focusFrame->setWidget(now);
			}
		});
	}
	//11：QFontComboBox，注意信号currentFontChanged
	{
		QFontComboBox *fontComboBox  = new QFontComboBox(window);
		layout->addWidget( fontComboBox );
		
		QObject::connect(fontComboBox, &QFontComboBox::currentFontChanged, [window](const QFont &newFont) {
			/*std::function<void(QWidget*, const QFont&)> setFontRecursive;
			setFontRecursive = [&setFontRecursive](QWidget *widget, const QFont &font) {
				widget->setFont(font);
				for (auto *childWidget : widget->findChildren<QWidget *>()) {
					setFontRecursive(childWidget, font);
				}
			};
			//不能直接设置，可能会报错，要写安全代码,还是不行
			window->layout()->setEnabled(false);
			setFontRecursive(window, newFont);
			window->layout()->activate();
			window->layout()->setEnabled(true);*/
			QApplication::setFont(newFont);
		});
	}
	//12:QFontDialog:这个是Dialog，能弹出对话框，Module型的。
	{
		QPushButton* button = new QPushButton("Font Dialog");
		
		// 连接信号槽来响应复选框的状态变化 对应lambda为function<void()>
		QObject::connect(button, &QPushButton::clicked, [window,button](){
			QFontDialog * dialog = new QFontDialog;
			dialog->setWindowTitle("Simple Dialog");
			dialog->setModal(true); // 设置为模态对话框
			//dialog->setAttribute(Qt::WA_DeleteOnClose);//关闭时，Qt自动删除它。一定要去掉，否则selectedFont就失败了。
			//dialog->setOption(QFontDialog::NoButtons);//这个也要去掉，没有OK就没有Accepted了。
			if( dialog->exec() == QDialog::Accepted){
				button->setFont( dialog->selectedFont() );
			}
		});
		layout->addWidget(button);
	}
	//13：QFormLayout，布局器，Qt支持布局器嵌套
	if( dynamic_cast<QBoxLayout*>(layout) || dynamic_cast<QGridLayout*>(layout))
	{
		// 创建表单布局并添加一些小部件
		QFormLayout *formLayout = new QFormLayout;
		QLineEdit *firstNameLineEdit = new QLineEdit;
		QLineEdit *lastNameLineEdit = new QLineEdit;
		formLayout->addRow("First name:", firstNameLineEdit);
		formLayout->addRow("Last name:", lastNameLineEdit);
		if(  QBoxLayout *boxLayout = dynamic_cast<QBoxLayout*>(layout) )//这个是if let的语法糖了。
			boxLayout->addLayout(formLayout);
		else if( QGridLayout *gridLayout =  dynamic_cast<QGridLayout*>(layout) ) 
			gridLayout->addLayout(formLayout,0,0);
	}
	#if 0 //QFrame 不应该出现在这里，只是功能测试而已
	//14：QFrame 装饰物，支持CSS语法，与FocusFrame进行对比学习，对比一下 它基本是个window啊
	{
		QFrame * frame = new QFrame;
		//设置属性
		frame->setFrameShape(QFrame::Box); // 设置框架形状为盒型
		frame->setFrameShadow(QFrame::Raised); // 设置框架阴影为凸起效果
		//line
		frame->setLineWidth(1); // 设置外边线宽度
		frame->setMidLineWidth(0); // 设置中间线宽度
		//使用样式表进行自定义
		frame->setStyleSheet("QFrame { background-color: yellow; }");
		
		QVBoxLayout * frameLayout = new QVBoxLayout( frame );
		while( QLayoutItem * child = layout->takeAt(0)){
			frameLayout->addWidget( child->widget());
		}
		layout->addWidget(frame);
	}
	#endif
	//15：QGraphicsEffect的子类QDropShadowEffect 用例
	{
		// 创建一个按钮并设置一个阴影效果
		QPushButton *button = new QPushButton("Shadow Button");
		
		// 创建一个阴影效果实例
		QGraphicsDropShadowEffect *effect = new QGraphicsDropShadowEffect;
		effect->setBlurRadius(10); // 设置模糊半径
		effect->setColor(Qt::blue); // 阴影颜色
		effect->setOffset(5); // 设置阴影的偏移量

		// 将效果应用到按钮上
		button->setGraphicsEffect(effect);
		
		layout->addWidget(button);
	}
	//16:QGroupBox,感觉比ButtonGroup要简单一点的组件，用起来更麻烦一点，更好定制？
	{
		// 创建 QGroupBox 并设置标题
		QGroupBox *groupBox = new QGroupBox("Group Box Title");

		// 创建一些子控件
		QRadioButton *radio1 = new QRadioButton("Radio Button 1");
		QRadioButton *radio2 = new QRadioButton("Radio Button 2");
		QPushButton *button = new QPushButton("Push Button");

		// 创建一个垂直布局管理器
		QVBoxLayout *vbox = new QVBoxLayout;
		vbox->addWidget(radio1);
		vbox->addWidget(radio2);
		vbox->addWidget(button);
		//vbox->addStretch(1); // 添加弹性空间

		// 将布局设置到 QGroupBox
		groupBox->setLayout(vbox);
		
		layout->addWidget(groupBox);

	}
	//17：QInputDialog：弹出式界面，然后从文件路径中选择文件名的路径，或者文件夹
	{
		QPushButton* button = new QPushButton( "Input Dialog" );		
		// 连接信号槽来响应复选框的状态变化 对应lambda为function<void()>
		QObject::connect(button, &QPushButton::clicked, [window](){
			QStringList items;
			items << "Item1" << "Item2" << "Item3";

			bool ok;
			QString item = QInputDialog::getItem(
				window,                  // 父窗口
				"Input Dialog Title",    // 对话框标题
				"Select an item:",       // 提示信息
				items,                   // 可选项列表
				0,                       // 初始选中项索引
				false,                   // 下拉列表是否可编辑
				&ok                      // 用户是否点击了 OK
			);

			if (ok && !item.isEmpty()) {
				// 用户选择了一个有效的项并且点击了 OK
				qDebug() << "用户选择了："<<item;//自己带换行,不用加\n
			}
		});
		layout->addWidget(button);
	}
}
int main(int argc, char *argv[])
{
    QApplication app(argc, argv);

    // 创建一个窗口
    QWidget window;
    window.setWindowTitle(Title);
	window.resize(400,100);
    // 使用垂直布局
    QVBoxLayout *layout = new QVBoxLayout(&window);
    
    //这里是主要的过程，挨个添加在里面。
    addLayoutCommonSettings( layout, &window, &app);
	addLayOutWidgets( layout );
	
	//固定的信息
	QPushButton* button = new QPushButton("click=>quit!"); // 创建一个按钮，并设置显示文本
    QObject::connect(button, &QPushButton::clicked, &app, &QApplication::quit); // 连接按钮的clicked信号到QApplication的quit槽函数
	layout->addWidget(button);
    // 设置布局并显示窗口
    window.setLayout(layout);
    window.show();

    // 运行应用程序事件循环
    return app.exec();
}